Skip to content

feat(loop): ci-fix — automated bounded fix attempts for failing PR CI (issue #96, part 1) - #153

Merged
robercano-ghbot merged 2 commits into
mainfrom
feat/issue-96-ci-fix
Jul 17, 2026
Merged

feat(loop): ci-fix — automated bounded fix attempts for failing PR CI (issue #96, part 1)#153
robercano-ghbot merged 2 commits into
mainfrom
feat/issue-96-ci-fix

Conversation

@robercano-ghbot

Copy link
Copy Markdown
Collaborator

What this does

Part 1 of #96: gives the autonomous PR loop a third reaction to a
loop-authored, open PR — a failing CI check, with no pending human
feedback, now gets an automated fix attempt instead of sitting red
forever.

  • pr-ci-fix.sh (new): detects open, bot-authored PRs whose current
    head commit
    has a failing CI check (CheckRun or legacy StatusContext
    shape — same dual parse as merge-ready.sh's decide()), excluding:
    a PR that's also a pr-feedback.sh candidate (precedence, see
    below); PRs labeled needs-human (budget already exhausted) or
    claude-ci-fixing (fix already in flight); and a head whose fix has
    already been posted (<!-- claude-ci-addressed:<head_sha> --> marker
    comment, keyed to the current head SHA so a genuinely new failure on
    a new commit still re-triggers).
  • loop-census.sh: surfaces the count as ci_fix_prs=N, folded into
    the existing cadence=FAST trigger alongside feedback_prs.
  • loop-tick.sh: wires pr-ci-fix.sh in as step 5/5, parses its TSV
    (lowest-numbered PR wins, same tie-break as feedback), and emits
    action=ci-fix pr=N as a new verdict line.
  • loop-event.sh: builds the CI-FIX driver prompt — label
    claude-ci-fixing first (in-flight guard), fix on the same branch
    (no new branch), push, then post the claude-ci-addressed marker with
    the real head SHA. Never merges, never force-pushes.
  • loop-daemon.sh: gives ci-fix drivers their own transient systemd
    unit shape (pr-loop-driver-cifix-pr<N>, distinct from
    pr-loop-driver-pr<N> for feedback) so a feedback driver and a ci-fix
    driver on the same PR number can never collide in naming or reattach.
    ci-fix pr=N is excluded from the post-exit debris/phantom-branch
    verification (that check is for a freshly-created feat/issue-N-*
    branch; ci-fix pushes onto an existing PR branch, so there's nothing
    new to verify).
  • .claude/commands/pr-loop.md / .claude/self/pr-loop-self.md:
    STEP 0 tick contract updated to mention the new step and verdict.

Decision: verdict precedence

feedback > ci-fix > advance. Owner CHANGES_REQUESTED always
outranks everything else — a human is waiting on a reply, so a PR that
is both red-CI and awaiting unaddressed feedback is handled entirely
as feedback (excluded from pr-ci-fix.sh's own output, and enforced
again at the verdict level in loop-tick.sh — belt and suspenders).
CI-fix outranks a fresh advance dispatch. Merge still requires a fresh
owner approval regardless of any of this — merge-ready.sh is
untouched.

Bounded attempts

CI-fix dispatches reuse the same per-issue attempt-budget counter
introduced in #95 (loop-issue-attempts.json, keyed by the issue the
PR's branch was cut from) — no new state file. Once the budget is
exhausted, the PR is labeled needs-human via the existing
needs-human.sh seam from #99, exactly like the advance/feedback paths.

Out of scope (follow-ups)

Legs 2 (review-comment convergence) and 3 (conflict-after-sibling-merge
rebase) of #96 are not included here — separate follow-up PRs.

🤖 Generated with Claude Code

robercano and others added 2 commits July 17, 2026 09:22
Driver exited ~08:56Z 2026-07-16 without committing. Snapshot taken
during manual debris triage so the work can't be lost; needs review,
tests, and completion before any PR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… suite (issue #96)

Completes the WIP snapshot from a killed driver: pr-ci-fix.sh and its
wiring into loop-census.sh/loop-tick.sh/loop-daemon.sh/loop-event.sh
(verdict precedence feedback > ci-fix > advance, shared #95 per-issue
attempt budget, distinct pr-loop-driver-cifix-pr<N> transient unit)
were already complete after rebasing onto main (#146-#151); this adds
the test coverage that was still missing:

- loop-census.test.sh: ci_fix_prs=N passthrough from pr-ci-fix.sh.
- loop-tick.test.sh: ci-fix verdict selection, lowest-PR tie-break,
  ci-fix-beats-advance precedence, and its tick-record fields.
- loop-ceilings.test.sh: ci-fix shares the per-issue attempt budget
  with advance/feedback, and the full three-way precedence
  (feedback > ci-fix > advance) end to end through loop-tick.sh.
- loop-daemon.test.sh: driver_unit_name/verdict_from_unit_name for the
  ci-fix shape, verify_and_classify_post_exit pass-through, and a full
  driver-spawn integration scenario.
- loop-event.test.sh: the CI-FIX prompt variant (in-flight guard
  label, claude-ci-addressed marker instructions, one-shot contract).
- plan-gate.test.sh: fixtures updated for pr-ci-fix.sh's now-mandatory
  sibling-script presence.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@robercano-ghbot
robercano-ghbot merged commit 2d9459b into main Jul 17, 2026
9 checks passed
@robercano-ghbot
robercano-ghbot deleted the feat/issue-96-ci-fix branch July 17, 2026 07:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

module:harness Orchestrator machinery under .claude

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants